home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
graphics
/
3dvect37.zip
/
3D.DOC
next >
Wrap
Text File
|
1994-06-22
|
30KB
|
564 lines
"Knowledge is Power, Power is Money"
3d Vectors Source
by John McCarthy (with a little help from his mommy:eg food)
1316 Redwood Lane
Pickering, Ontario, Canada
L1X 1C5
(905) 831-1944 (voice, always willing to talk, but do not call at 2am)
Documentation is in no defined order. Sorry, I just sorta lumped my
ideas together and ended up with this file.
Routines support any x mode, - but page flipping is not allowed in
resolutions which allow only 1 page - see "pages" constant.
Full clipping is performed to user defind areas - see constants in
equ.inc. They have been changed to memory locations for variable
windowing or multiple screens. For windowing, the last z locations for
that window must be remembered along with a slew of other locations, see
vars.inc for that info. To change a window, save the lastz information,
reset with old lastz information and then call set_clip to change the
border clipping and screen center data.
The theoretical screen is considered to be (x,y) with 0,0 being the
center of the screen!. So -100,-100 is somewhere on the top left!
actual screen goes from (0,0) to (320,200) - or whatever mode size you
select. Matt Pritchard's routines (xmode.asm) assume 0,0 to be the top
left of the screen while my routines (me = John = 3d.asm) consider the
screen center to be the constants xcenter and ycenter.
Visible space is -4628196 to +4628196 on all axis (approx). Object
locations are 32 bit, vector routines are 16 bit, objects must be smaller
than 16 bit but are visable within about a 32 bit range. (4 million, as
it is now, is very very far). Since the camera is always at (0,0,0)
(relative), objects with (relative) negative z values are not seen. This
cuts the z space to 0 to 4mil. Visible space is always divided by 256 so
decimals can be allowed in adding, and moving of objects. Visible space
therefore, is actually from -1.024 billion to +1.024 billion with the
lower byte having no effect on the location. Non-visible space is where
objects can be but won't appear on screen. This space is a 256 *256*256
cube. To racap: you have 32 bit x,y,z axis with a visual range of 28
bits, where the lower 8 bits don't affect the location. (Lower 8 bits
don't count because locations are shr'ed) i say that the visable space
is "about" 4mil only because of the code in the make3d routine: this code
multiplies by a constant and then performs divide by z distance. We
cannot allow the multiply to overflow and therfore must truncate our
maximum distance to prevent this. The constants for multiplication are
the screen ratio constants and the calculation to test for an overflow is
as such -2^32/2/256/(largest constant). The constant I have used is 464
for the y ratio. I have used this because of my desire to use the
320x400 mode resolution. Therfore, 4.3gig/2/256/464 = about 4 million -
our maximum visual distance. Like, trust me, you don't really need a
larger universe. Fixing the make 3d routine wont allow you to see
farther because then you would have to fix the rotate routine, etc, etc.
When defining a location: ebx = x, ecx = y, ebp = z
When defining a rotation: x = pitch, y = heading, z = yaw
si refers to object number, di refers to time.
Rotations occure in order:
zobject,xobject,yobject,ycamera,xcamera,zcamera - rotations are
compounded in matrix for faster computation.
Vmatrix is the matrix for object rotation. Ematrix is the matrix for
camera rotation. If you want know where a point in space will show up
on the screen, load ebx, ecx, ebp with your x,y,z point, subtract camera
location and call erotate (eye rotate). The point will be rotated
according to current camera angles. Make sure that a call to setsincose
has taken place to set the eye rotation matrix (ematrix).
Polygon can handle any number of sides. To draw a triangle, make last
point equal to first point, eg 1,4,5,1. Number of sides of a polygon is
determined so that the polygon is not finished until the last side
equals the first side: eg 1,7,6,14,13,4,2,1 would be a 7 sided polygon.
The constant maxsurfaces determines the maximum number of surfaces an
object can have. The constant maxpolys determines the maximum number of
connections a surface can have.
Sample shape data:
headerthing dd -1 ; distance that first resolution is visable, -1 = last
dd offset thing - offset $ - 4
thing dw 6 ; number of points
dw 4 ; number of surfaces
dw 25 dup (?) ; future use
dw x,y,z ; point 0
dw x,y,z ; point 1
dw x,y,z
...
dw command
dw texture for side 1
dw texture for side 2
dw colour for side 1
dw colour for side 2
dw connection data eg (1,2,3,4,1)
dw [?,?,?] [optional surface normal if shade command used]
dw more connection data...
...
There are several commands one can use for each surface. Commands like
steel texture, always visible, opposite colours, etc. View the objects
include file to see what/how to use them.
Bitmaps can be part of an object or be made as seperate objects. I will
be using the bitmaps for things like explosions, smoke (from damaged
planes/spaceships) and distant suns/solar system (U know, like in x-wing)
set the values bitx and bity to the scaling to be used for each bitmap
and set userotate to himap as this is the command to define a bitmaped
object. vxs and vys are the additional scaling used for individual
objects (vxs+bitx = final scaling factor). When part of an object, use
dw himap/lomap, point #, x scale, y scale. Remember, scaling is added to
bitx and bity so objects have a base scale plus some individual scale.
Complex objects don't cut it for speed! keep your objects simple and you
can have more of them on screen at once! maximum speed is found with low
resolutions. High resolutions with clipped borders also provide adaquate
speed. A shallow but wide screen (small y, big x) provides better usage
of cpu time than a tall and thin screen. One big object is faster to
compute than many small objects (if same surface area) an object viewed
from the side takes signifiganly less time to compute than if viewed
from the top due to the shallow y, large x idea. Object shapes have
abcd prefixes. Therefore, as object gets farther from camera, less
points/surface must be calculated. You must define shapes for every
distance. You can have only one distance/shape if you want or you can
have hundreds.
eg dd distance1
dd offset thing1 - offset $ - 4
dd distance2
dd offset thing2 - offset $ - 4
dd -1 ; <- -1 is last distance flag
dd offset thing3 - offset $ - 4
Surface data must be entered counter clockwize so side will be visible.
Clockwize surfaces are visible from other side and will not be plotted
(unless you use a surface command override, see objects.inc)
An increase in screen objects increases cpu time. However, if you know
that you will always have the screen filled (in the case of floors, and
runways.) You can disable the clear_fill routine during those parts! if
the screen will be covered with background walls and such, there is no
purpose to call the clear routine to compute the next part! i have
therefore added a flag for the clear_fill routine to use: when your
animation comes to the part when your looking at the ground or walls (and
there are NO empty spaces) toggle the flag to skip clear_fill and get
more cpu time. This also works if you are approaching an object or
large surface, since the new object will totaly cover the previous one.
Another time trick is to have your main background object include the sky
(or area to be cleared) as part of the object. If you are going to have
walls that go halfway up the screen, have them go halfway with the
regular walls and then make another surface that goes to the top of the
screen (or above if you want to move around) with the colour 0. You can
then deactivate the clear_fill routine and still have the animation
appear as if the walls are completely seperate objects.
Sorting routine for objects (as opposed to sides) uses last z value to
re-sort for the next plot. If you plan on drawing static pictures you
may want to call makobjs twice to: 1) draw and find zeds, sort, then 2)
re-draw. This will be the only way (and easiest way) to plot an accurate
picture of what we have. Don't worry about calling twice during
animations as the first picture will be the only picture that is not
sorted. During animations, all objects are sorted properly, based on
previous z.
Routines which are expected to be used in animations have been optimized
but routines intended for use as background and title draw routines are
not intended to be fast.
PLEASE DOCUMENT YOUR CHANGES!!
Newfollow routine does not handle object lock on well if object is
accelerating. The routine calculates where the object will be in di
frames and attempts to point the camera to it in di frames. However, if
the object is accelerating, then the object will not be where it was
expected to be at that time. So the camera must re-lock on to its
target. This loop commences until the camera actually has locked on to
the target object, from this point on, the camera will follow the object
regardless of motion. The re-lock on sequence takes the last number of
frames and divides it by two, so the re-lock on loop will move toward an
accelerating object at an accelerating rate.
General overview: locations are 32 bit -2.1Gig to +2.1Gig, angles are
16bit from 0-65535 degrees, 4 quadrants - 4096 entries each quadrant.
Variables in vector routine are 16bit. cosine and sine list are words
but get converted into doublewords when used.
Some public routines: (not all, just some) See the top of the .asm files
for complete lists of public routines.
arctan +/*% arctan(rise/run)=arctan(cx/ax). any quadrant, 16bit
calc_angles +/*% calculate xy angles between object di and object si
calc_middle +/*% calculate xy angles between object di and ebx,ecx,ebp
checkfront +/*% test points (di,bp) (si,qds) (dx,qes) for clockwize
clear_fill +/ clears write page using xupdate and yupdate variables
compound + *% compounds angles of eye and angles of object into
matrix
cosine +/*% eax=cos(eax), 16bit input, 32bit output
drawvect + draw list of vectors using points, sides and order
erotate +/*% rotate for angles of eye, 32bit, uses ematrix
fakedraw +/ draw line in firstbyte and lastbyte tables from xy1
to xy2
flip_page +/ flip between pages 0 and 1, wait for vertical sync
get_displacement +/*% calculate difference between objects
initfont # initialize font pointers
initpages # initialize x-mode pages for flip_page to page 0
loadpoints + *% load points into array, rotate and translate as we go
loadsurfs + * load surfaces, check if visible as we go
look_at_it +/* immediatly force eyeax, eyeay to look at object
wherelook
make1obj + * make object si
make3d +/*% make bx,cx,bp into bx,cx 2d pair, 16bit
makeobjs + make all objects then sorts based on last z location
move_to # /* move object si to bx,cx,bp - time di frames
newfollow # /* forces camera to follow object si, time to get there
di
poly_fill +/ uses oney,firstbyte and lastbyte to draw one surface
point_it +/* point object si at object di
point_dir +/* point object si in direction it is moving
point_to +/* point object si at location ebx,ecx,ebp
set_speed +/* calculate velocity based on angles
point_time +/* point obj di to bx,cx,bp in di frames
put_object +/* put object esi at location ebx,ecx,ebp
re_sort + sorts objects based on "finalzed" values
rotate +/*% rotate bx,cx,bp (x,y,z) through matrix vrotate, 16bit
set_finall +/* calculate xsfinal for object (location)
set_finala +/* calculate vxsfinal for object (angles)
setmakeorder # resets order for makeobjs - for initialization
setsincose +/ set sin and cos multipliers for eye rotations
setupbase # set up object base pointers to shapes
set_object_on /* turn object si on
set_object_off /* opposite
sine +/*% ax=sin(ax), 16bit input, 32bit output
show_stars +/ display stars in background
sort_list + sorts list of sides of polygon
twist_si +/* set angular velocity based on ebx,ecx,ebp and di(time)
updvectors +/ updates vector xyz's and angles
where_si +/*% return location of where object will be in di frames
Legend:
# used for initialization of code or new scene
+ used regularly in animation loop
/ can be used by user outside of animation loop if needed
* routine requires parameters passed in registers
% routine exits with results in registers
> routine wipes harddrive
There are more routines at the end of 3d.asm for more general functions
like find the camera displacement and finding rotational offsets between
two objects. U figure them out - fairly self explanatory. Also check out
poly.inc for more 3d functions and math.inc for general math functions.
Drawvect routine has a seperate routine for drawing lines (as opposed to
surfaces). The fake_line routine and poly_fill routine could do the job
but they were too slow. The line was drawn twice then filled just like a
polygon but now a seperate routine clipps and draws. If you need/want
to use this line drawing routine it has been seperated from the
draw_vect routine. I do not use the xmode line draw by Matt Pritchard
as it does not allow for clipping.
Sin and cosin tables - 90 degrees is 16384, 180=32768...
Move_si routine - to move an object around, load up ebx, ecx and ebp with
the x,y,z locations of where you want the object to end up. Load di with
the time you would like the object to take to get there. Load si with the
object number you want to move and call move_si. The updvectors routine
does the rest!
To look at an object. either 1) put the object number in wherelook. or
2) load si with the object to look at, load di with the time to move the
camera to the object, and call new_follow.
Just think, only 7 months ago (march '93), i had trouble programming a
batch file!
Shape data can be almost as large as you need it 'till it crashes. try a cube
20000x20000x20000. Calculations use 32 bit registers and can handle
up to 16 bit locations. keeping the object size small will allow a larger
visible space. but larger objects will allow you to get closer with more
accuracy in the mathematics of rotations.
List of command bits to date: (for object definitions)
note: "visible" = "points appear counter-clockwise"
texture definitions:
0 - normal surface, no features, constant colour.
wavey - steel texture for surface 0 = none, colour offset determines
screen offset for texture. eg 16+7 will use colour block 16-31
but make the sine wave texture 14 (7*2) lines down. this is so
all sine wave textures do not appear on the same line.
windows and engines look good with this feature.
shade - lambert shading bit, must have normal calculated or at least
have free space for pre_cal_lambert to use:
eg 128,16*1,1,2,3,1, ?,?,?<- these 3 words are surface normal!
inverse - inversion bit for shading option. 0=normal shading, 1=inverse
if option +4 is used, inversion automatically occures when
other side is displayed.
glow - =shade+inverse
last - colour has same colour as previous surface (used when
you want gourad shading, but want to avoid duplicate
calculations - don't set gourad bit if this is what
you use it for.) when this is used, the colour number
determines the new colour block to use. the shading
of this colour will be the same as the surface before
it, but the colour block can be different.
mesh - screen door style of surface
stone - texture map style surface, I just liked the way it looked..
glenz - glenz vector surface - uses cross referancing palette for
changing on screen colours.
commands:
point - defines a single point; must be repeated! eg dw 64,col,3,3
line - if used, defines a line (must be set to define a true line)
himap - if set, defines a bitmap,eg: point #, bitmap #, x scale,y scale
lomap - uses 1/4 scaled bitmap (every 4'th pixel is sampled), fast
iterate - generate iteration if side visible (iteration = sub-object)
both - side is always visible no matter angle, skips counter-clowise test
- "both sides have same texture"
double - side is always visible but other side has high byte colour
"double sided surface"
note: if this is used, option "both" must not be used!!
onscr - test if side is on screen - don't use if all points are
outside clipping parameters.
check - dont plot this side, just use as test points for visibility.
this is mostly used with iterations.
matrix - generate new matrix
sub-object - make subobject
gosub - transfer loading of surface data elsewhere
return - return from gosub
There are a kazillion more options - look in the objects.inc file for the
surface types and example objects.
There are two kinds of bitmaps and points. Those which are inside objects
and those which are seperate objects themselves. if userotate object command
is set to himap/point,then the entire object is considered as a point
or bitmap. But if userotate is not set this way, then a normal object is
drawn and bitmaps then come from within the object definitions (below). this
way, bitmaps and points can be either part of a larger object, or they are
computed fast on their own. (eg explosions and bullets as seperate objects)
Note: When writing surface descriptions, try to make the first value unique
from any other first value. this way, the sort routine will give a more
accurate sorting of sides. eg 1,3,6,1 2,4,1,2 rather than 1,3,6,1 1,2,4,1
to recap:
0 = constant colour, only visible from counter-clockwise side
wavey = sine texture
shade = shading - requires 3 blank words for surface normal eg dw 0,0,0
inverse = invert the shading direction, 0=normal, 1=sun is other way.
last = use intensity from previous surface (not colour, only intensity)
point = point
line = line
himap = bitmap (scalable, non-rotatable)
lomap = bitmap (scalable, non-rotatable)
iterate = generate iteration if side visible
both = always visible
double = always visible but other side has high byte colour,"double sided"
onscr = plot side only if all the following points are on the screen
check = dont plot side but use the following points as a test for visiblity
What you can't mix on a single surface: "double" with "both"!!
You do not have to define a point for the center of the object. the point
0 defines the center of the object. This is different from earlier versions
Remember that negative y (-y) is up, +y is down. This is opposite from our
regular grade 13 mathematics but it is consistant with the computers screen
arrangement. If your objects look funny, make sure you have this correct.
The shading for objects requires that 3 words be present after the side
definition. These 3 words represent the surface normal to the side and can
be either set by you (slow and tedious) by using the normal.bas program, or
can be set up by the routine pre_cal_lambert. This routine scans the object
to find surfaces that have the shading bit set (128). The surface normal
will be calculated for that surface and stored in the 3 words set aside by
you. If you remove the shading feature from a surface, make sure you remove
the extra 3 words or it will screw up. But you knew that right. To use the
routine pre_cal_lambert, load si with the object you wish to scan, and call.
make sure objbase points to the offset of the object. si will be the object
number, eg 0,1,2,3, not the offset. The offset will come from objbase.
Each on-screen object can have it's own colour palette scheme by setting the
palxref offsets to a cross reference palette. this uses the xlat command to
take the objects colour, and xlat it into a new colour based on the xref
table you provide. each object can have it own xref table. This way, many
on-screen objects can use the same shape data while each is coloured with a
different colour scheme (5 stuka airplanes all coloured differently, for
example) You will find the palxref tables (for each object remember) in the
file equ.inc. A routine set_xref_palette has been provided for you so you
can set the xref offset (this routine is in poly.inc)
To have a bitmap on the screen (scalable but non-rotatable) use option himap
in either userotate or as an object command. Lomap uses the same bitmap
method but only draws every 4'th pixel - good for explosions and smoke.
onoff commands:
0 = object off eg mov onoff[esi],2 ; turn on sub object
1 = main object on
2 = sub object on
userotate object commands:
0 = all rotations supported - full object
1 = camera rotations only - no compound, new loadpoints
object is same as when userotate=0 but will not allow any object specific
rotations. this is used to speed up rendering of objects that are
stationary or objects that will always be pointing in the same direction.
make1obj routine then assumes angles = 0x, 0y, 0z
if this is used to define a sub-object (see main.asm for that green box
with those two sub-object blocks) then +1 option in userotate makes the
box angle/rotation independant from the main object.
himap = bitmap - no compound, no loadpoints, no sort and no drawvect
lomap = bitmap, 1/4 scale, faster than 32
if object is bitmap, then:
whatshape - indexer to which bitmap in bitbase list
xs,ys,zs - point to bitmap location in space
vxs - bitmap scaling (how big is bitmap). note: bitmap is already
scaled based on distance so you don't have to change this
as the bitmap gets farther away.
point = point - no compound, no loadpoints, no sort and no drawvect
used for bullets. could be used for stars but if you do want to make
stars, make a specialized bitmap routine. making stars as objects would
be too slow. right now, bullets all have same colour, see constant in
equ.inc. note:sept 29/93, stars routines now are a seperate specialized
assembley routine. - stars.asm
xs,ys,zs - point to bullet location in space
The locations of sub_objects are x/256 because of the way conversion between
real space co-ordinates and object co-ordinates are calculated. Objects
are 256 times larger than their appearance in real space. This allows much
greater accuracy in real space!! Eg , if you have a cube 500x500x
500 (which is really 1000 wide cause -500 to +500 is 1000 units) then, in
order to "move" accros the surface, you should only have to move 1000 units,
right? Well here, you'll have to move 256000 units even though the cube is
only 1000 units wide/tall/deep. This is because the real space co-ordinates
are 24 bit (32bit/256). This gives you greater accuracy in placing objects
where you want them in space but still allows you to have very large objects
If your objects appear jittery when viewing close-up, make them physically
larger. For example, double all the point data (makes object twice the
size). This way, the multiply routine has more accuracy when calculating
rotations/3dpoints.
Example of how to draw a single polygon:
p1x equ -50
p1y equ -50
p2x equ -90
p2y equ 70
p3x equ 60
p3y equ 80
mov x1,p1x
mov y1,p1y
mov x2,p2x
mov y2,p2y
call fakeline ; draw in buffer
mov x1,p2x
mov y1,p2y
mov x2,p3x
mov y2,p3y
call fakeline ; draw in buffer
mov x1,p3x
mov y1,p3y
mov x2,p1x
mov y2,p1y
call fakeline ; draw in buffer
mov colq,7
mov steel,-1
call poly_fill ; fill polygon on screen
call flip_page ; show it...
A little note about polygon construction:
Polygons can only have angles less than 180 degrees. That is, they must be
round like circles, not hooky like a sickle. eg:
/\
/\ \ \ invalid polygon
\ \ valid polygon / \
\ \ \/\ \
\/ \/
^
|---------this angle is greater than 180degrees.
if you want to make this here^^^^, you must split it up into 2:
/\ /\
\ \ \ \
/\ + \ \ = / \
\/ \ \ \/\ \
\/ \/
So, an object like so: ____
/ __ \
/_/ \_\
must be drawn like so:
____ ____
/\ + /\ + \__/ = / __ \
/_/ \_\ /_/ \_\
Question: How come my objects appear upside-down
Answer: Thats because the Y axis is inverted. Positive X points to the
right, positive Z points away from you and positive Y points down.
Question: Ok, I make the Y value point down, now they still don't look
right - the surfaces are connected correctly but it looks wrong.
Answer: The most probable reason for this type of error is that the
connection data has been entered in reverse order. Eg 3,4,5,2,7,3
should probably have been entered as 3,7,2,5,4,3. Remember, enter
the surfaces in Counter-Clock-Wise order.
If you don't like manual data entry (which I don't) you can try out the
converter DXF23DV which should be supplied with this package. Now just draw
your objects in AutoCAD and it will convert them into assembley connections
Remember, in AutoCAD +z points up, +y point away, in my package, +z points
away, +y points down. Draw the objects in Autocad as if +z is up and my
converter will change the co-ordinate system for you.
Question: The objects look great from far away, but up close, the appear
"jumpy" or the whole thing disappears.
Answer: Your using too small a scale factor. If you have a cube 10x10x10,
the math routines dont have much room to calculate a rotated object
Try making your objects bigger, like 1000x1000x1000. The reason
why the entire object may disappear is that the center of the
object has passed to close to the camera. If you have large
objects, you may want to cut them up into seperate sections and
piece them together as a seperate objects.